package cn.jbolt.core.Interceptor.reqlimit.redis;

import cn.jbolt.core.Interceptor.reqlimit.JBoltRateLimiter;
import cn.jbolt.core.Interceptor.reqlimit.JBoltRateResult;
import cn.jbolt.core.api.JBoltApiBaseController;
import cn.jbolt.core.api.JBoltApiRet;
import cn.jbolt.core.base.config.JBoltConfig;
import cn.jbolt.core.kit.JBoltControllerKit;
import cn.jbolt.core.util.JBoltIpUtil;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;

import java.lang.reflect.Method;

/**
 * JBolt redis 内置限流封装
 */
public class JBoltRedisRequestRateLimitInterceptor implements Interceptor {
    private static final Log LOG = Log.getLog(JBoltRedisRequestRateLimitInterceptor.class);
    private boolean isOnlyLimitApiController;
    public JBoltRedisRequestRateLimitInterceptor(){}
    public JBoltRedisRequestRateLimitInterceptor(boolean isOnlyLimitApiController){
        this.isOnlyLimitApiController = isOnlyLimitApiController;
    }
    @Override
    public void intercept(Invocation inv) {
        //如果限制了只针对api的controller拦截 就判断是不是JBoltApiBaseController
        if(isOnlyLimitApiController && !(inv.getController() instanceof JBoltApiBaseController)){
            inv.invoke();
            return;
        }
        //1、action上的限流优先级最高
        Method action = inv.getMethod();
        boolean isActionLimit = action.isAnnotationPresent(JBoltRateLimiter.class);
        JBoltRateLimiter rateLimiter = null;
        if(isActionLimit){
            rateLimiter = action.getAnnotation(JBoltRateLimiter.class);
        }else{
            //如果action上没有 就用Controller身上的
            Class controllerClass = inv.getController().getClass();
            boolean isControllerLimit = controllerClass.isAnnotationPresent(JBoltRateLimiter.class);
            if(isControllerLimit){
                rateLimiter = (JBoltRateLimiter) controllerClass.getAnnotation(JBoltRateLimiter.class);
            }
        }
        if(rateLimiter!=null){
            JBoltRateResult result = limitIt(rateLimiter,inv);
            if(result.isNotPass()){
               if(JBoltConfig.DEV_MODE){
                   LOG.info("限流启动... limiter:"+ JsonKit.toJson(rateLimiter));
               }
                Controller controller = inv.getController();
                // 处理跨域实际请求
                JBoltControllerKit.processCrossOrigin(controller, action);
                String reqMethod = controller.getRequest().getMethod();
                // 处理预检请求
                if (JBoltControllerKit.isOptions(reqMethod)) {
                    controller.renderJson(JBoltApiRet.OPTIONS);
                    return;
                }
                JBoltControllerKit.renderFail(controller,result.getMsg());
                return;
            }
        }
        inv.invoke();
    }

    /**
     * 执行检测限制
     * @param limiter
     * @param inv
     * @return
     */
    private JBoltRateResult limitIt(JBoltRateLimiter limiter,Invocation inv) {
        String actionKey = inv.getActionKey();
        JBoltRateResult result = null;
        switch (limiter.limitType()){
            case DEFAULT:
                result = defaultLimit(limiter,actionKey);
                break;
            case IP:
                result = ipLimit(limiter,actionKey,JBoltIpUtil.getIp(inv.getController().getRequest()));
                break;
            default:
                result = JBoltRateResult.CHECK_EXCEPTION;
                break;

        }
        return result;
    }

    /**
     * 执行默认限流检测
     * @param limiter
     * @param actionKey
     * @return
     */
    private JBoltRateResult defaultLimit(JBoltRateLimiter limiter, String actionKey) {
        //前缀+actionkey
        String key = limiter.prefix() + actionKey;
        return JBoltRedisRateLimitUtil.limit(key,limiter.time(),limiter.count(),limiter.msg());
    }

    /**
     * 执行IP限流检测
     * @param limiter
     * @param actionKey
     * @param ip
     * @return
     */
    private JBoltRateResult ipLimit(JBoltRateLimiter limiter, String actionKey, String ip) {
        if(StrKit.isBlank(ip)){
            return JBoltRateResult.NO_IP;
        }
        //前缀+actionkey+ip
        String key = limiter.prefix() + actionKey+"_"+ip;
        return JBoltRedisRateLimitUtil.limit(key,limiter.time(),limiter.count(),limiter.msg());
    }

}
